#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define NBINS 128
#define BINMAPSHIFT      5
#define BITSPERMAP       (1U << BINMAPSHIFT)
#define BINMAPSIZE       (NBINS / BITSPERMAP)

#define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \
                          ? __alignof__ (long double) : 2 * SIZE_SZ)

#define NSMALLBINS         64
#define SMALLBIN_WIDTH    MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
#define MIN_LARGE_SIZE    ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)

#define in_smallbin_range(sz)  \
  ((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)

#ifndef offsetof
# define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
#endif


#ifndef INTERNAL_SIZE_T
# define INTERNAL_SIZE_T size_t
#endif

#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))


/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1

/* extract inuse bit of previous chunk */
#define prev_inuse(p)       ((p)->mchunk_size & PREV_INUSE)


/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2

/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
 *    from a non-main arena.  This is only set immediately before handing
 *       the chunk to the user, if necessary.  */
#define NON_MAIN_ARENA 0x4

#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)

/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))

/* Like chunksize, but do not mask SIZE_BITS.  */
#define chunksize_nomask(p)         ((p)->mchunk_size)

struct malloc_chunk {

  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};
  
/* The corresponding bit mask value.  */
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)

/* The smallest possible chunk */
#define MIN_CHUNK_SIZE        (offsetof(struct malloc_chunk, fd_nextsize))

/* The smallest size we can malloc is an aligned minimal chunk */
#define MINSIZE  \
  (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))

#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)

/* Like chunksize, but do not mask SIZE_BITS.  */
#define chunksize_nomask(p)         ((p)->mchunk_size)
/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))

#define chunk_at_offset(p, s)  ((mchunkptr) (((char *) (p)) + (s)))

typedef struct malloc_chunk* mchunkptr;

#define PRINT_META_CONTAIN_BKFD(p) printf("p = %p, mem = %p, p->mchunk_prev_size = %x, p->mchunk_size = %x, p->fd = %p, p->bk = %p, prev_inuse = %d\n",\
				p, ((char*)p + 2*SIZE_SZ), p->mchunk_prev_size,\
				chunksize(p), p->fd, p->bk, prev_inuse(p)) 

#define PRINT_META(p) printf("p = %p, mem = %p, p->mchunk_prev_size = %x, p->mchunk_size = %x, prev_inuse = %d\n",\
				p, ((char*)p + 2*SIZE_SZ), p->mchunk_prev_size,\
				chunksize(p), prev_inuse(p)) 
#define PRINT_LINE()	printf("\n")

int main() {

	struct malloc_chunk fake_chunk __attribute__ ((aligned (MINSIZE)));
	fake_chunk.mchunk_size = 0x18;
	printf("fake chunk = %p, fake chunk->fd = %p\n", &fake_chunk, &fake_chunk.fd);
	void* ptr1, *ptr2;
	ptr1 = malloc(0x10); // fastbin 
        ptr1 = (void*) &fake_chunk.fd;

	mchunkptr p1 = (mchunkptr)(ptr1 - 2*SIZE_SZ);
	INTERNAL_SIZE_T size = chunksize (p1);
	mchunkptr p = chunk_at_offset (p1, size);
	INTERNAL_SIZE_T nomask_size = (mchunkptr)chunk_at_offset (p, size)->mchunk_size;
	printf("chunk size = %x, next chunk = %p, next chunk nomask size = %x, next chunk size = %x\n",
			 size, p, nomask_size, chunksize (p));	

	free(ptr1);
	
	ptr2 = malloc(0x10);
	
	mchunkptr p2 = (mchunkptr) (ptr2 - 2*SIZE_SZ);
	PRINT_META_CONTAIN_BKFD(p2);

	return 0;	
}
